package com.hzw.saas.service.product.service;

import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Resource;

import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.api.product.ILicenseProductService;
import com.hzw.saas.api.product.IProductService;
import com.hzw.saas.api.product.dto.LicenseProductBO;
import com.hzw.saas.api.product.dto.LicenseProductExpireBO;
import com.hzw.saas.api.product.dto.ProductDto;
import com.hzw.saas.api.property.IPropertyService;
import com.hzw.saas.api.property.bo.PropertyBO;
import com.hzw.saas.api.property.enmus.SysDictKeyEnum;
import com.hzw.saas.api.property.enmus.SysDictModuleEnum;
import com.hzw.saas.common.config.util.MybatisPlusUtil;
import com.hzw.saas.common.util.enums.AssertEnum;
import com.hzw.saas.service.product.mapper.LicenseProductStructMapper;
import com.hzw.saas.service.product.model.LicenseProduct;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import ma.glasnost.orika.MapperFacade;

/**
 * <p>
 *
 * </p>
 *
 * @author sonam
 * @since 2021/4/8 13:52
 */
@RequiredArgsConstructor
@Service("licenseProductServiceImpl")
public class LicenseProductServiceImpl extends ServiceImpl<LicenseProductStructMapper, LicenseProduct> implements ILicenseProductService, IService<LicenseProduct> {

    private final MapperFacade mapperFacade;

    @Resource(name = IPropertyService.SYS_DICT_BEAN)
    private IPropertyService<SysDictKeyEnum> propertyService;

    @Resource
    private IProductService productService;

    @Override
    public String getIdByName(String productName, AssertEnum assertEnum) {
        LambdaQueryChainWrapper<LicenseProduct> query = super.lambdaQuery()
            .eq(LicenseProduct::getProductName, productName.trim());
        MybatisPlusUtil.limitOne(query);
        LicenseProduct licenseProduct = query.one();
        AssertEnum.assertResult(assertEnum, licenseProduct, "产品");
        return Objects.isNull(licenseProduct) ? null : licenseProduct.getProductId();
    }

    @Override
    public LicenseProductBO getByLicenseId(String licenseId) {
        LicenseProduct licenseProduct = this.getById(licenseId);
        LicenseProductBO licenseProductBO = mapperFacade.map(licenseProduct, LicenseProductBO.class);
        this.assembleExtraInfo(licenseProductBO);
        return licenseProductBO;
    }

    @Override
    public List<LicenseProductBO> listLicenseByLevel(String levelId) {
        if (StrUtil.isBlank(levelId))
            return new ArrayList<>();
        List<LicenseProduct> list = this.listDO(levelId, null);
        return this.convert2BO(list);
    }

    @Override
    public List<LicenseProductBO> listLicenseByLevel(String levelId, int optionalType) {
        if (StrUtil.isBlank(levelId))
            return new ArrayList<>();
        List<LicenseProduct> list = this.listDO(levelId, optionalType);
        return this.convert2BO(list);
    }

    @Override
    public List<LicenseProductExpireBO> listLicenseByLevelWithExpireDate(String levelId) {
        if (StrUtil.isBlank(levelId))
            return new ArrayList<>();
        List<LicenseProduct> list = this.listDO(levelId, null);
        return this.convert2ExpireBO(list);
    }

    @Override
    public List<LicenseProductExpireBO> listLicenseByLevelWithExpireDate(String levelId, int optionalType) {
        if (StrUtil.isBlank(levelId))
            return new ArrayList<>();
        List<LicenseProduct> list = this.listDO(levelId, optionalType);
        return this.convert2ExpireBO(list);
    }

    private List<LicenseProduct> listDO(String levelId, Integer optionalType) {
        return super.lambdaQuery()
            .eq(LicenseProduct::getLevelId, levelId)
            .eq(Objects.nonNull(optionalType), LicenseProduct::getOptionalType, optionalType)
            .list();
    }

    private List<LicenseProductBO> convert2BO(List<LicenseProduct> entities) {
        List<LicenseProductBO> boList = mapperFacade.mapAsList(entities, LicenseProductBO.class);
        this.assembleExtraInfo(boList);
        return boList;
    }

    private List<LicenseProductExpireBO> convert2ExpireBO(List<LicenseProduct> entities) {
        List<LicenseProductExpireBO> boList = mapperFacade.mapAsList(entities, LicenseProductExpireBO.class);
        this.assembleExtraInfo(boList);
        return boList;
    }

    private void assembleExtraInfo(LicenseProductBO bo) {
        // product
        if (Objects.isNull(bo)) {
            return;
        }
        Optional<ProductDto> product = Optional.ofNullable(productService.getProductById(bo.getProductId()));
        bo.setIconName(product.map(ProductDto::getIconName).orElse(""));
        // product parallel model
        String parallelModelText = propertyService.getPropertyValue(
            SysDictModuleEnum.PRODUCT_PARALLEL_MODEL.getModule(), String.valueOf(bo.getParallelModel()));
        bo.setParallelModelText(parallelModelText);
    }

    private void assembleExtraInfo(List<? extends LicenseProductBO> boList) {
        // products
        List<String> productIds = boList.stream().map(LicenseProductBO::getProductId).collect(Collectors.toList());
        List<ProductDto> products = productService.listProducts(productIds);
        // product parallel model
        List<PropertyBO> parallelModels = propertyService.listProperties(SysDictModuleEnum.PRODUCT_PARALLEL_MODEL.getModule());
        for (LicenseProductBO bo : boList) {
            // product icon
            ProductDto product = products.stream()
                .filter(p -> Objects.equals(p.getProdId(), bo.getProductId()))
                .findAny().orElse(null);
            if (Objects.nonNull(product)) {
                bo.setIconName(product.getIconName());
            }
            // parallelModelText
            PropertyBO parallelModel = parallelModels.stream()
                .filter(p -> Objects.equals(p.getKey(), String.valueOf(bo.getParallelModel())))
                .findAny().orElse(null);
            if (Objects.nonNull(parallelModel)) {
                bo.setParallelModelText(parallelModel.getValue());
            }
            if (bo instanceof LicenseProductExpireBO) {
                ((LicenseProductExpireBO) bo).setValidatedExpiredDate(DateUtil.offsetMonth(new Date(), bo.getValidPeriod()));
            }
        }
    }

}
